package edu.unc.comp410.colemangeer.data;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import edu.unc.comp410.colemangeer.model.Customer;
import edu.unc.comp410.sharedcode.OrderedPair;

/**
 * Class for reading a list of customers and a list of zipcodes from files, and performing referential
 * integrity validation on the zipcodes in all of the customer entries
 * 
 * Finds customers whose zip codes are not in the zip code list,
 * and, for customers whose zipcodes ARE in the zip code list, verifies
 * whose city and State values do not differ differ from the zip code list's city and state values
 * for that zip code
 */

public class BusinessValidation {

	//Error message catalog
	private static final String CUSTOMER_ZIP_NOT_IN_FILE = "Zip Code not in zip table for customer:\n\t";
	private static final String CITY_STATE_MISMATCH = "City/State mismatch in zip table for customer:\n\t";
	
	// Data access objects
	private ZipGatewayEmulator zipEmulator;
	private CustomerGatewayEmulator customerEmulator;
	
	
	// Customers for validation
	List<Customer> customersToCheck;
	
	// Canonical zip code - city - state list for validation
	List<Zip> zipCodesForValidation;
	
	// custom view of zip code list, associating a zipcode with a set of (city, state) values
	// this structure can handle a one-to-many relationship between zipcode and (city, state),
	// which doesn't seem to ever occur in the input data we were given
	Map<String,Set<OrderedPair<String,String>>> zipCodeToCityStateMap;
	
	/**
	 * Default constructor
	 */
	public BusinessValidation(){
		customersToCheck = new ArrayList<Customer>();
		zipCodesForValidation = new ArrayList<Zip>();
		zipCodeToCityStateMap = new HashMap<String, Set<OrderedPair<String,String>>>();
	}
	
	/**
	 * Initialize list of customer objects
	 * @throws FileNotFoundException
	 */
	public void initCustomerList() throws FileNotFoundException{	
		customerEmulator = new CustomerGatewayEmulator();
		customersToCheck.addAll(customerEmulator.custTable.values());
	}
	
	/**
	 * Initialize list of zip code objects
	 * @throws FileNotFoundException
	 */
	public void initZipList() throws FileNotFoundException{
		zipEmulator = new ZipGatewayEmulator();
		zipCodesForValidation.addAll(zipEmulator.zipTable.values());
	}
	
	/**
	 * Initialize the zip code to city/state data structure
	 * For each unique zip code in the input, associate one or more (City, StateAbbrev) ordered pairs 
	 * with that zip code
	 */
	public void initZipCityStateMap(){
		for (Zip z: zipCodesForValidation){
			// is there already a value set for this zipCode?
			Set<OrderedPair<String,String>> cityStatePair = zipCodeToCityStateMap.get(z.getZipCode());
			if (cityStatePair==null){
				// no: add the zipCode
				HashSet<OrderedPair<String,String>> newSet = new HashSet<OrderedPair<String,String>>();
				newSet.add(new OrderedPair<String, String>(z.getCity(),z.getStateAbbrev()));
				zipCodeToCityStateMap.put(z.getZipCode(), newSet);
			}
			else{
				// yes: add the (city,state) value to the zipCode's entry set
				cityStatePair.add(new OrderedPair(z.getCity(),z.getStateAbbrev()));
			}
		}
	}
	
	/**
	 * check for customer zipcode in zip table
	 * if it is there, check integrity of customer city/state values
	 * if not there, report error
	 */
	private void validateCustomerZipCodes(){		
		for (Customer c: customersToCheck){
			if (!zipCodeToCityStateMap.containsKey(c.getZipCode())){
				System.out.println(CUSTOMER_ZIP_NOT_IN_FILE + c.toString());
			}
			else{
				validateCityAndStateIntegrity(c);
			}
		}
	}
	
	/** 
	 * check for integrity of customer city/state values
     *  if they don't match the zip table's values, report error
	 */
	private void validateCityAndStateIntegrity(Customer c){
		
		Set<OrderedPair<String,String>> correctCityStateSet;
		correctCityStateSet = this.zipCodeToCityStateMap.get(c.getZipCode());
		OrderedPair<String,String> customerVals = new OrderedPair<String,String>(c.getCity(),c.getStateAbbrev());
		if (!correctCityStateSet.contains(customerVals)){
			System.out.println(CITY_STATE_MISMATCH + c.toString());
		}
	}
	
	public static void main(String[] args) {
		BusinessValidation validator =  new BusinessValidation();
		
		try {
			validator.initCustomerList();
			validator.initZipList();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		validator.initZipCityStateMap();	
		validator.validateCustomerZipCodes();
	}

}
